
/* Written by Michael E. Kounavis, Commmunications Technology Lab, 
   Intel Corporation, 2008 
 */ 

#include <stdio.h> 
#include <stdlib.h> 
#include "int_types.h" 
#include "vortex_misc.h" 
#include "vortex.h" 
#include "SHA3api_ref.h" 

#include "speed_test.h" 

#define ALGORITHM_NAME "vortex" 
#define	MAX_MARKER_LEN		50 
#define	SUBMITTER_INFO_LEN	128 

#ifdef OPTIMIZED_ASSEMBLY 

void vortex_256_asm(unsigned char *hash, unsigned char *data, unsigned char *rcon, int length); 
void vortex_512_asm(unsigned char *hash, unsigned char *data, unsigned char *rcon, int length); 

#endif 

/* The following global variables represent the tunable parameters of the vortex algorithm 
 */ 

uint8_t number_of_rounds_g;			/* The number of rounds each modified AES block is executed */ 
uint8_t mul_type_g;					/* The type of multiplication used: 0 carryless, 1 integer */ 
uint8_t degree_of_diffusion_g;		/* The number of times diffusion happens in the vortex last sub-block */ 
uint8_t *a0_b0_32_g;				/* The initial value of the chaining variable for vortex 224 and vortex 256 */ 
uint8_t *ta_tb_32_g;				/* The final tweak value for vortex 224 and vortex 256 */ 
uint8_t *a0_b0_64_g;				/* The initial value of the chaining variable for vortex 384 and vortex 512 */ 
uint8_t *ta_tb_64_g;				/* The final tweak value for vortex 384 and vortex 512 */ 
uint8_t *rcon_256_g[MAX_NUMBER_OF_ROUNDS];		/* The round constants used in the key schedule algorithm */ 
uint8_t *rcon_512_g[MAX_NUMBER_OF_ROUNDS]; 


/*	Routine for deriving a vortex hash value. It uses the parameters assigned to the 
	global variables above */ 


int 
vortex(uint8_t *data, 
			   uint64_t *length, 
			   uint8_t *hash, 
			   uint32_t type) 
{ 
	uint8_t number_of_rounds; 
	uint8_t mul_type; 
	uint8_t a0_b0[64]; 
	uint8_t ta_tb[64]; 
	uint8_t degree_of_diffusion; 

	if ((!data) || (!hash) || (!length)) 
	{ 
		perror("vortex(): null pointer passed\n"); 
		return VORTEX_NULL_POINTER_ERROR; 
	} 
	number_of_rounds = number_of_rounds_g; 
	mul_type = mul_type_g; 
	degree_of_diffusion = degree_of_diffusion_g; 

	switch(type) 
	{ 
		case 224: 
		case 256: 
			varcpy(a0_b0, a0_b0_32_g, 32); 
			varcpy(ta_tb, ta_tb_32_g, 32); 
			break; 
		case 384: 
		case 512: 
			varcpy(a0_b0, a0_b0_64_g, 64); 
			varcpy(ta_tb, ta_tb_64_g, 64); 
			break; 
		default: 
			perror("vortex(): bad hash type\n"); 
			return VORTEX_BAD_HASH_TYPE; 
	} 

	return tunable_vortex( 
		data, 
		length, 
		hash, 
		type, 
		number_of_rounds, 
		mul_type, 
		a0_b0, 
		ta_tb, 
		degree_of_diffusion); 
} 

/*	Generic routine for deriving a vortex hash value. It accepts parameters as input operands. 
	This is used by the Hash() function of the NIST cryptographic API */ 

int 
tunable_vortex(uint8_t  *data, 
			   uint64_t *length, 
			   uint8_t  *hash, 
			   uint32_t type, 
			   uint8_t  number_of_rounds, 
			   uint8_t  mul_type, 
			   uint8_t  *a0_b0, 
			   uint8_t  *ta_tb, 
			   uint8_t  degree_of_diffusion) 
{ 

	if ((!data) || (!hash) || (!length) || (!a0_b0) || (!ta_tb)) 
	{ 
		perror("tunable_vortex(): null pointer passed\n"); 
		return VORTEX_NULL_POINTER_ERROR; 
	} 

	switch(type) 
	{ 
		case 224: 
			return vortex_224( 
				data, 
				length, 
				hash, 
				number_of_rounds, 
				mul_type, 
				a0_b0, 
				ta_tb, 
				degree_of_diffusion); 
		case 256: 
			return vortex_256( 
				data, 
				length, 
				hash, 
				number_of_rounds, 
				mul_type, 
				a0_b0, 
				ta_tb, 
				degree_of_diffusion); 

		case 384: 
			return vortex_384( 
				data, 
				length, 
				hash, 
				number_of_rounds, 
				mul_type, 
				a0_b0, 
				ta_tb, 
				degree_of_diffusion); 

		case 512: 
			return vortex_512( 
				data, 
				length, 
				hash, 
				number_of_rounds, 
				mul_type, 
				a0_b0, 
				ta_tb, 
				degree_of_diffusion); 
		default: 
			perror("tunable_vortex(): bad hash type\n"); 
			return VORTEX_BAD_HASH_TYPE; 
	} 
} 


int 
vortex_224(uint8_t  *data, 
		   uint64_t *length, 
		   uint8_t  *hash, 
		   uint8_t  number_of_rounds, 
		   uint8_t  mul_type, 
		   uint8_t  *a0_b0, 
		   uint8_t  *ta_tb, 
		   uint8_t  degree_of_diffusion) 
{ 

	uint8_t hash256[32];	 

	if( vortex_256( 
		data, 
		length, 
		hash256, 
		number_of_rounds, 
		mul_type, 
		a0_b0, 
		ta_tb, 
		degree_of_diffusion) != VORTEX_SUCCESS) 
	{ 
		perror("vortex_224(): vortex_256 call failed\n"); 
		return VORTEX_CALL_FAILURE; 
	} 
	/* The 224-bit hash derives from the 256-bit by truncation */ 
	varcpy(hash, hash256, 28);	 
	return VORTEX_SUCCESS; 
} 


int 
vortex_384(uint8_t  *data, 
		   uint64_t *length, 
		   uint8_t  *hash, 
		   uint8_t  number_of_rounds, 
		   uint8_t  mul_type, 
		   uint8_t  *a0_b0, 
		   uint8_t  *ta_tb, 
		   uint8_t  degree_of_diffusion) 
{ 

	uint8_t hash512[64];	 
	 
	if( vortex_512( 
		data, 
		length, 
		hash512, 
		number_of_rounds, 
		mul_type, 
		a0_b0, 
		ta_tb, 
		degree_of_diffusion) != VORTEX_SUCCESS) 
	{ 
		perror("vortex_384(): vortex_512 call failed\n"); 
		return VORTEX_CALL_FAILURE; 
	} 
	/* The 384-bit hash derives from the 512-bit by truncation */ 
	varcpy(hash, hash512, 48); 
	return VORTEX_SUCCESS; 
} 


int 
vortex_256(uint8_t  *data, 
		   uint64_t *length_pointer, 
		   uint8_t  *hash, 
		   uint8_t  number_of_rounds, 
		   uint8_t  mul_type, 
		   uint8_t  *a0_b0, 
		   uint8_t  *ta_tb, 
		   uint8_t  degree_of_diffusion) 
{ 
	uint64_t length; 
	uint32_t number_of_zeros; 
	uint64_t blocks_no_malloc; 
	uint32_t blocks_malloc; 
	uint8_t	 last_block[64]; 
	uint8_t  last_bytes; 
	uint8_t  last_byte_index; 
	uint64_t i; 

	length = length_pointer[0]; 
	varset(last_block, 0, 64); 

	/* number_of_zeros = the number of bits equal to 0 required for padding */ 
 
	if (length <= 191) 
		number_of_zeros = (uint32_t)(191-length); 
	else 
		number_of_zeros = (uint32_t)(512 - ((length-191) % 512)); 
	 
	/* blocks_no_malloc = vortex blocks for which no additional memory allocations need to be made; 
	   We assume that the input buffer may not always contain enough bytes for the padding 
	 */ 
	 
	blocks_no_malloc = length/512; 
	blocks_malloc = (uint32_t)((length + number_of_zeros + 65)/512 - blocks_no_malloc); 
	 
	init_hash_256(hash, a0_b0);	 

	/* NOTE: The optimized assembly routine below uses stand-ins for future processor instructions for AES round 
	   and Galois Field multiplication. It is used for measuring expected performance from processors coming out 
	   at the the 2009-2010 time frame. Also the code uses the default parameters of the number_of_rounds=3 
	   and mul_type = 0 
	 */ 

#ifdef OPTIMIZED_ASSEMBLY 
	vortex_256_asm((unsigned char *)hash, (unsigned char *)data, (unsigned char *)rcon_256_g, (int)blocks_no_malloc); 
#else 
	for(i=0; i< blocks_no_malloc; i++) 
		vortex_256_block(hash, &data[i*64], number_of_rounds, mul_type); 
#endif 

	last_bytes = (uint8_t)math_ceil((length%512)/8.0); 
	 
	if(!(length%8)) 
		last_byte_index = last_bytes; 
	else 
		last_byte_index = last_bytes-1; 
	 
	for(i=0; i< blocks_malloc; i++) 
	{ 
		/*do the padding in case blocks_malloc = 1 */ 
		varcpy(last_block, &data[blocks_no_malloc*64], last_bytes);	 
		last_block[last_byte_index] |= 1 << (length%8); 
		vortex_256_block(hash, last_block, number_of_rounds, mul_type); 
	} 
	 
	varset(last_block, 0, 64); 

	/* Copy the current hash to the last block according to the 
	   Enveloped Merkle Damgard construction 
	 */ 

	varcpy(last_block, hash, 32); 
	if(!blocks_malloc) 
	{ 
		/* do the padding in case blocks_malloc = 0 */ 
		varcpy(&last_block[32], &data[blocks_no_malloc*64], last_bytes);	 
		last_block[last_byte_index+32] |= 1 << (length%8); 
	} 
	*(uint64_t *)&last_block[56] = length; 
	varcpy(hash, ta_tb, 32); 
	last_vortex_256_block(hash, last_block, number_of_rounds, mul_type, degree_of_diffusion); 
	return VORTEX_SUCCESS; 
} 

int 
vortex_256_block(uint8_t *hash, 
				 uint8_t *data, 
				 uint8_t number_of_rounds, 
				 uint8_t mul_type) 
{ 
	vortex_256_sub_block(hash, data, number_of_rounds, mul_type); 
	vortex_256_sub_block(hash, &data[32], number_of_rounds, mul_type); 
	return VORTEX_SUCCESS; 
} 

int 
last_vortex_256_block(uint8_t *hash, 
					  uint8_t *data, 
					  uint8_t number_of_rounds, 
					  uint8_t mul_type, 
					  uint8_t degree_of_diffusion) 
{ 
	vortex_256_sub_block(hash, data, number_of_rounds, mul_type); 
	last_vortex_256_sub_block(hash, &data[32], number_of_rounds, mul_type, degree_of_diffusion); 
	return VORTEX_SUCCESS; 
} 

int 
vortex_256_sub_block(uint8_t *hash, 
					 uint8_t *data, 
					 uint8_t number_of_rounds, 
					 uint8_t mul_type) 

{ 
	uint8_t i, j; 
	uint8_t a[16], b[16]; 
	uint8_t key_a[16], key_b[16]; 
	uint8_t state_a[16], state_b[16]; 

	if(number_of_rounds > MAX_NUMBER_OF_ROUNDS) 
	{ 
		perror("vortex_256_sub_block(): number of rounds exceeds limit\n"); 
		return VORTEX_INVALID_ROUNDS; 
	} 

	varcpy(a, hash, 16); 
	varcpy(b, &hash[16], 16); 

	for(i=0; i < 2; i++) 
	{ 
		varcpy(key_a, a, 16); 
		varcpy(key_b, b, 16); 
		varcpy(state_a, &data[i*16], 16); 
		varcpy(state_b, &data[i*16], 16); 

		for(j=0; j < number_of_rounds; j++) 
		{	 
			/* prepare the key schedule for the modified AES transformation */ 
			padd_128(key_a, key_a, rcon_256_g[j]); 
			sbox_128(key_a, key_a); 
			perm_128(key_a, key_a); 
			 
			/* call the AES round routine */ 
			rijndael_128_round(key_a, state_a); 

			padd_128(key_b, key_b, rcon_256_g[j]); 
			sbox_128(key_b, key_b); 
			perm_128(key_b, key_b); 
			rijndael_128_round(key_b, state_b); 
		} 
		/* Matyas-Meyer-Oseas */ 
		pxor_128(a, state_a, &data[i*16]); 
		pxor_128(b, state_b, &data[i*16]); 
		 
		/* The Merging function of Vortex */ 
		vma_256(a, b, mul_type); 
	} 
	varcpy(hash, a, 16); 
	varcpy(&hash[16], b, 16); 
	return VORTEX_SUCCESS; 
} 

/* The routine below is the same as vortex_sub_block but the last stage of AES rounds and 
   merging is repeated degree_of_diffusion times to strengthen the processing of the 
   last block 
 */ 	 

int 
last_vortex_256_sub_block(uint8_t *hash, 
						  uint8_t *data, 
						  uint8_t number_of_rounds, 
						  uint8_t mul_type, 
						  uint8_t degree_of_diffusion) 

{ 
	uint8_t i, j; 
	uint8_t a[16], b[16]; 
	uint8_t key_a[16], key_b[16]; 
	uint8_t state_a[16], state_b[16]; 

	if(number_of_rounds > MAX_NUMBER_OF_ROUNDS) 
	{ 
		perror("vortex_256_sub_block(): number of rounds exceeds limit\n"); 
		return VORTEX_INVALID_ROUNDS; 
	} 

	varcpy(a, hash, 16); 
	varcpy(b, &hash[16], 16); 

	varcpy(key_a, a, 16); 
	varcpy(key_b, b, 16); 
	varcpy(state_a, &data[0], 16); 
	varcpy(state_b, &data[0], 16); 

	for(j=0; j < number_of_rounds; j++) 
	{	 
		padd_128(key_a, key_a, rcon_256_g[j]); 
		sbox_128(key_a, key_a); 
		perm_128(key_a, key_a); 
		rijndael_128_round(key_a, state_a); 

		padd_128(key_b, key_b, rcon_256_g[j]); 
		sbox_128(key_b, key_b); 
		perm_128(key_b, key_b); 
		rijndael_128_round(key_b, state_b); 
	} 
	pxor_128(a, state_a, &data[0]); 
	pxor_128(b, state_b, &data[0]); 
	vma_256(a, b, mul_type); 

	for(i=0; i < degree_of_diffusion; i++) 
	{ 
		varcpy(key_a, a, 16); 
		varcpy(key_b, b, 16); 
		varcpy(state_a, &data[16], 16); 
		varcpy(state_b, &data[16], 16); 

		for(j=0; j < number_of_rounds; j++) 
		{	 
			padd_128(key_a, key_a, rcon_256_g[j]); 
			sbox_128(key_a, key_a); 
			perm_128(key_a, key_a); 
			rijndael_128_round(key_a, state_a); 
 
			padd_128(key_b, key_b, rcon_256_g[j]); 
			sbox_128(key_b, key_b); 
			perm_128(key_b, key_b); 
			rijndael_128_round(key_b, state_b); 
		} 
		pxor_128(a, state_a, &data[16]); 
		pxor_128(b, state_b, &data[16]); 
		vma_256(a, b, mul_type); 
	} 
	varcpy(hash, a, 16); 
	varcpy(&hash[16], b, 16); 
	return VORTEX_SUCCESS; 
} 

/* The merging function of vortex */ 

int 
vma_256(uint8_t *a, 
		uint8_t *b, 
		uint8_t mul_type) 
{ 
	uint8_t inner_product[16]; 
	uint8_t outer_product[16]; 

	mul_128(inner_product, 
			&a[8], 
			&b[0], 
			mul_type); 
	mul_128(outer_product, 
			&a[0], 
			&b[8], 
			mul_type); 

	*(uint64_t *)&a[0] ^=  *(uint64_t *)&inner_product[0]; 
	*(uint64_t *)&a[8] ^=  *(uint64_t *)&outer_product[8]; 
	*(uint64_t *)&b[0] +=  *(uint64_t *)&outer_product[0]; 
	*(uint64_t *)&b[8] +=  *(uint64_t *)&inner_product[8]; 
	return VORTEX_SUCCESS; 
} 

/* The code structure of vortex 512 is identical to that of vortex 256. For comments see the 
   code above. 
 */ 

int 
vortex_512(uint8_t  *data, 
		   uint64_t *length_pointer, 
		   uint8_t  *hash, 
		   uint8_t  number_of_rounds, 
		   uint8_t  mul_type, 
		   uint8_t  *a0_b0, 
		   uint8_t  *ta_tb, 
		   uint8_t  degree_of_diffusion) 
{ 
	uint64_t length; 
	uint32_t number_of_zeros; 
	uint64_t blocks_no_malloc; 
	uint32_t blocks_malloc; 
	uint8_t	 last_block[128]; 
	uint8_t  last_bytes; 
	uint8_t  last_byte_index; 
	uint64_t i; 

	if(length_pointer[1]) 
	{ 
		perror("vortex_512(): vortex_512 length not supported by the implementation\n"); 
		return VORTEX_INVALID_LENGTH; 
	} 
	length = length_pointer[0]; 
	varset(last_block, 0, 128); 
	if (length <= 383) 
		number_of_zeros = (uint32_t)(383-length); 
	else 
		number_of_zeros = (uint32_t)(1024 - ((length-383) % 1024)); 
	blocks_no_malloc = length/1024; 
	blocks_malloc = (uint32_t)((length + number_of_zeros + 129)/1024 - blocks_no_malloc); 
	 
	init_hash_512(hash, a0_b0);	 
#ifdef OPTIMIZED_ASSEMBLY 
	vortex_512_asm((unsigned char *)hash, (unsigned char *)data, (unsigned char *)rcon_512_g, (int)blocks_no_malloc); 
#else 
	for(i=0; i< blocks_no_malloc; i++) 
		vortex_512_block(hash, &data[i*128], number_of_rounds, mul_type); 
#endif 

	last_bytes = (uint8_t)math_ceil((length%1024)/8.0); 
	 
	if(!(length%8)) 
		last_byte_index = last_bytes; 
	else 
		last_byte_index = last_bytes-1; 
	 
	for(i=0; i< blocks_malloc; i++) 
	{ 
		varcpy(last_block, &data[blocks_no_malloc*128], last_bytes);	 
		last_block[last_byte_index] |= 1 << (length%8); 
		vortex_512_block(hash, last_block, number_of_rounds, mul_type); 
	} 
	 
	varset(last_block, 0, 128); 
	varcpy(last_block, hash, 64); 
	if(!blocks_malloc) 
	{ 
		varcpy(&last_block[64], &data[blocks_no_malloc*128], last_bytes);	 
		last_block[last_byte_index+64] |= 1 << (length%8); 
	} 
	*(uint64_t *)&last_block[120] = length; 
	varcpy(hash, ta_tb, 64); 
	last_vortex_512_block(hash, last_block, number_of_rounds, mul_type, degree_of_diffusion); 
	return VORTEX_SUCCESS; 
} 

int 
vortex_512_block(uint8_t *hash, 
				 uint8_t *data, 
				 uint8_t number_of_rounds, 
				 uint8_t mul_type) 
{ 
	vortex_512_sub_block(hash, data, number_of_rounds, mul_type); 
	vortex_512_sub_block(hash, &data[64], number_of_rounds, mul_type); 
	return VORTEX_SUCCESS; 
} 

int 
last_vortex_512_block(uint8_t *hash, 
					  uint8_t *data, 
					  uint8_t number_of_rounds, 
					  uint8_t mul_type, 
					  uint8_t degree_of_diffusion) 
{ 
	vortex_512_sub_block(hash, data, number_of_rounds, mul_type); 
	last_vortex_512_sub_block(hash, &data[64], number_of_rounds, mul_type, degree_of_diffusion); 
	return VORTEX_SUCCESS; 
} 

int 
vortex_512_sub_block(uint8_t *hash, 
					 uint8_t *data, 
					 uint8_t number_of_rounds, 
					 uint8_t mul_type) 

{ 
	uint8_t i, j; 
	uint8_t a[32], b[32]; 
	uint8_t key_a[32], key_b[32]; 
	uint8_t state_a[32], state_b[32]; 

	if(number_of_rounds > MAX_NUMBER_OF_ROUNDS) 
	{ 
		perror("vortex_512_sub_block(): number of rounds exceeds limit\n"); 
		return VORTEX_INVALID_ROUNDS; 
	} 
 
	varcpy(a, hash, 32); 
	varcpy(b, &hash[32], 32); 

	for(i=0; i < 2; i++) 
	{ 
		varcpy(key_a, a, 32); 
		varcpy(key_b, b, 32); 
		varcpy(state_a, &data[i*32], 32); 
		varcpy(state_b, &data[i*32], 32); 

		for(j=0; j < number_of_rounds; j++) 
		{	 
			padd_256(key_a, key_a, rcon_512_g[j]); 
			sbox_256(key_a, key_a); 
			perm_256(key_a, key_a); 
			rijndael_256_round(key_a, state_a); 

			padd_256(key_b, key_b, rcon_512_g[j]); 
			sbox_256(key_b, key_b); 
			perm_256(key_b, key_b); 
			rijndael_256_round(key_b, state_b); 
		} 
		pxor_256(a, state_a, &data[i*32]); 
		pxor_256(b, state_b, &data[i*32]); 
		vma_512(a, b, mul_type); 
	} 
	varcpy(hash, a, 32); 
	varcpy(&hash[32], b, 32); 
	return VORTEX_SUCCESS; 
} 

int 
last_vortex_512_sub_block(uint8_t *hash, 
						  uint8_t *data, 
						  uint8_t number_of_rounds, 
						  uint8_t mul_type, 
						  uint8_t degree_of_diffusion) 

{ 
	uint8_t i, j; 
	uint8_t a[32], b[32]; 
	uint8_t key_a[32], key_b[32]; 
	uint8_t state_a[32], state_b[32]; 

	if(number_of_rounds > MAX_NUMBER_OF_ROUNDS) 
	{ 
		perror("vortex_512_sub_block(): number of rounds exceeds limit\n"); 
		return VORTEX_INVALID_ROUNDS; 
	} 

	varcpy(a, hash, 32); 
	varcpy(b, &hash[32], 32); 

	varcpy(key_a, a, 32); 
	varcpy(key_b, b, 32); 
	varcpy(state_a, &data[0], 32); 
	varcpy(state_b, &data[0], 32); 

	for(j=0; j < number_of_rounds; j++) 
	{	 
		padd_256(key_a, key_a, rcon_512_g[j]); 
		sbox_256(key_a, key_a); 
		perm_256(key_a, key_a); 
		rijndael_256_round(key_a, state_a); 

		padd_256(key_b, key_b, rcon_512_g[j]); 
		sbox_256(key_b, key_b); 
		perm_256(key_b, key_b); 
		rijndael_256_round(key_b, state_b); 
	} 
	pxor_256(a, state_a, &data[0]); 
	pxor_256(b, state_b, &data[0]); 
	vma_512(a, b, mul_type); 

	for(i=0; i < degree_of_diffusion; i++) 
	{ 
		varcpy(key_a, a, 32); 
		varcpy(key_b, b, 32); 
		varcpy(state_a, &data[32], 32); 
		varcpy(state_b, &data[32], 32); 

		for(j=0; j < number_of_rounds; j++) 
		{	 
			padd_256(key_a, key_a, rcon_512_g[j]); 
			sbox_256(key_a, key_a); 
			perm_256(key_a, key_a); 
			rijndael_256_round(key_a, state_a); 

			padd_256(key_b, key_b, rcon_512_g[j]); 
			sbox_256(key_b, key_b); 
			perm_256(key_b, key_b); 
			rijndael_256_round(key_b, state_b); 
		} 
		pxor_256(a, state_a, &data[32]); 
		pxor_256(b, state_b, &data[32]); 
		vma_512(a, b, mul_type); 
	} 
	varcpy(hash, a, 32); 
	varcpy(&hash[32], b, 32); 
	return VORTEX_SUCCESS; 
} 


int 
vma_512(uint8_t *a, 
		uint8_t *b, 
		uint8_t mul_type) 
{ 
	uint8_t inner_product[32]; 
	uint8_t outer_product[32]; 

	mul_256(inner_product, 
			&a[16], 
			&b[0], 
			mul_type); 
	mul_256(outer_product, 
			&a[0], 
			&b[16], 
			mul_type); 

	*(uint64_t *)&a[0]  ^=  *(uint64_t *)&inner_product[0]; 
	*(uint64_t *)&a[8]  ^=  *(uint64_t *)&inner_product[8];	 
	*(uint64_t *)&a[16] ^=  *(uint64_t *)&outer_product[16]; 
	*(uint64_t *)&a[24] ^=  *(uint64_t *)&outer_product[24]; 
	*(uint64_t *)&b[0]  +=  *(uint64_t *)&outer_product[0]; 
	*(uint64_t *)&b[8]  +=  *(uint64_t *)&outer_product[8]; 
	*(uint64_t *)&b[16] +=  *(uint64_t *)&inner_product[16]; 
	*(uint64_t *)&b[24] +=  *(uint64_t *)&inner_product[24]; 

	return VORTEX_SUCCESS; 
} 

int 
generate_constants(void) 
{ 
	int i, j; 
	uint64_t p, x; 
	a0_b0_32_g = (uint8_t *)malloc(32); 
	a0_b0_64_g = (uint8_t *)malloc(64); 
	ta_tb_32_g = (uint8_t *)malloc(32); 
	ta_tb_64_g = (uint8_t *)malloc(64); 

	for(i=0; i < MAX_NUMBER_OF_ROUNDS; i++) 
	{ 
		rcon_256_g[i] = (uint8_t *)malloc(16); 
		rcon_512_g[i] = (uint8_t *)malloc(32); 
	} 

	p = 4294967291; 
	x = 1414213562; 
	x = (x*x-x) % p; 

	for(i=0; i < 8; i++) 
	{ 
		((uint32_t *)a0_b0_32_g)[i] = (uint32_t)x; 
		x = (x*x-x) % p; 
	} 
	for(i=0; i < MAX_NUMBER_OF_ROUNDS; i++) 
		for(j=0; j < 4; j++) 
		{ 
			((uint32_t *)(rcon_256_g[i]))[j] = (uint32_t)x; 
			x = (x*x-x) % p; 
		} 
	for(i=0; i < 16; i++) 
	{ 
		((uint32_t *)a0_b0_64_g)[i] = (uint32_t)x; 
		x = (x*x-x) % p; 
	} 
	for(i=0; i < MAX_NUMBER_OF_ROUNDS; i++) 
		for(j=0; j < 8; j++) 
		{ 
			((uint32_t *)(rcon_512_g[i]))[j] = (uint32_t)x; 
			x = (x*x-x) % p; 
		} 
	for(i=0; i < 8; i++) 
	{ 
		((uint32_t *)ta_tb_32_g)[i] = (uint32_t)x; 
		x = (x*x-x) % p; 
	} 
	for(i=0; i < 16; i++) 
	{ 
		((uint32_t *)ta_tb_64_g)[i] = (uint32_t)x; 
		x = (x*x-x) % p; 
	} 
	return VORTEX_SUCCESS; 
} 

int 
generate_random_tweak_values(void) 
{ 
	int i; 
	ta_tb_32_g = (uint8_t *)malloc(32); 
	ta_tb_64_g = (uint8_t *)malloc(64); 

	for(i=0; i < 8; i++) 
		((uint32_t *)ta_tb_32_g)[i] = 
			(uint32_t)((double)(1<<31)*((double)rand())/((double)RAND_MAX)); 

	for(i=0; i < 16; i++) 
		((uint32_t *)ta_tb_64_g)[i] = 
			(uint32_t)((double)(1<<31)*((double)rand())/((double)RAND_MAX)); 

	return VORTEX_SUCCESS; 
} 

int 
set_default_parameters(void) 
{ 
	number_of_rounds_g = 10; 
	mul_type_g = 1; 
	degree_of_diffusion_g = 5; 
	return VORTEX_SUCCESS; 
} 

//////////////////////////////////////////////////////////////////////////////////// 
typedef enum { KAT_SUCCESS = 0, KAT_FILE_OPEN_ERROR = 1, KAT_HEADER_ERROR = 2, KAT_DATA_ERROR = 3, KAT_HASH_ERROR = 4 } STATUS_CODES; 

STATUS_CODES    genShortMsg(int hashbitlen); 
STATUS_CODES    genLongMsg(int hashbitlen); 
STATUS_CODES    genExtremelyLongMsg(int hashbitlen); 
STATUS_CODES    genMonteCarlo(int hashbitlen); 
int             FindMarker(FILE *infile, const char *marker); 
int             ReadHex(FILE *infile, BitSequence *A, int Length, char *str); 
void    fprintBstr(FILE *fp, char *S, BitSequence *A, int L); 

STATUS_CODES 
main() 
{ 
        int             i, ret_val,  bitlens[4] = { 224, 256, 384, 512 }; 
        unsigned long timeStart = clock(); 
        for ( i=0; i<4; i++ ) { 
                printf("%lums:\n\t", clock() - timeStart); 
                if ( (ret_val = genShortMsg(bitlens[i])) != KAT_SUCCESS ) 
                        return ret_val; 
                printf("%lums:\n\t", clock() - timeStart); 
                if ( (ret_val = genLongMsg(bitlens[i])) != KAT_SUCCESS ) 
                        return ret_val; 
                printf("%lums:\n\t", clock() - timeStart); 
                if ( (ret_val = genExtremelyLongMsg(bitlens[i])) != KAT_SUCCESS ) 
                        return ret_val; 
                printf("%lums:\n\t", clock() - timeStart); 
                if ( (ret_val = genMonteCarlo(bitlens[i])) != KAT_SUCCESS ) 
                        return ret_val; 
        } 
        printf("overall test time: %lums", clock() - timeStart); 
        return KAT_SUCCESS; 
} 

STATUS_CODES 
genShortMsg(int hashbitlen) 
{ 
        char            line[SUBMITTER_INFO_LEN]; 
        int                     msglen, msgbytelen, done; 
        BitSequence     Msg[256], MD[64]; 
        FILE            *fp_in; 
        char            logFileName [100]; 
        FILE            *logFile; 

        sprintf(logFileName, "%s_genShortMsg_%d.log", ALGORITHM_NAME, hashbitlen); 
        printf("logging results to: %s\n", logFileName); 
        logFile = fopen(logFileName, "w"); 

        if ( (fp_in = fopen("ShortMsgKAT.txt", "r")) == NULL ) { 
                printf("Couldn't open <ShortMsgKAT.txt> for read\n"); 
                return KAT_FILE_OPEN_ERROR; 
        } 

        if ( FindMarker(fp_in, "# Algorithm Name:") ) { 
                fscanf(fp_in, "%[^\n]\n", line); 
        } 
        else { 
                printf("genShortMsg: Couldn't read Algorithm Name\n"); 
                return KAT_HEADER_ERROR; 
        } 
        if ( FindMarker(fp_in, "# Principal Submitter:") ) { 
                fscanf(fp_in, "%[^\n]\n", line); 
        } 
        else { 
                printf("genShortMsg: Couldn't read Principal Submitter\n"); 
                return KAT_HEADER_ERROR; 
        } 

        done = 0; 
        timeTest(0, logFile);   //test start 
        do { 
                if ( FindMarker(fp_in, "Len = ") ) 
                        fscanf(fp_in, "%d", &msglen); 
                else { 
                        done = 1; 
                        break; 
                } 
                msgbytelen = (msglen+7)/8; 

                if ( !ReadHex(fp_in, Msg, msgbytelen, "Msg = ") ) { 
                        printf("ERROR: unable to read 'Msg' from <ShortMsgKAT.txt>\n"); 
                        return KAT_DATA_ERROR; 
                } 
                timeTest(1, logFile);   //unit test start 
                Hash(hashbitlen, Msg, msglen, MD); 
                timeTest(2, logFile);   //unit test stop 
        } while ( !done ); 
        timeTest(3, logFile);   //test stop 
        fclose(logFile); 
        printf("\tfinished ShortMsgKAT for <%d>\n", hashbitlen); 

        fclose(fp_in); 

        return KAT_SUCCESS; 
} 

STATUS_CODES 
genLongMsg(int hashbitlen) 
{ 
        char            line[SUBMITTER_INFO_LEN]; 
        int                     msglen, msgbytelen, done; 
        BitSequence     Msg[4288], MD[64]; 
        FILE            *fp_in; 
        char            logFileName [100]; 
        FILE            *logFile; 

        sprintf(logFileName, "%s_genLongMsg_%d.log", ALGORITHM_NAME, hashbitlen); 
        printf("logging results to: %s\n", logFileName); 
        logFile = fopen(logFileName, "w"); 

        if ( (fp_in = fopen("LongMsgKAT.txt", "r")) == NULL ) { 
                printf("Couldn't open <LongMsgKAT.txt> for read\n"); 
                return KAT_FILE_OPEN_ERROR; 
        } 

        if ( FindMarker(fp_in, "# Algorithm Name:") ) { 
                fscanf(fp_in, "%[^\n]\n", line); 
        } 
        else { 
                printf("genLongMsg: Couldn't read Algorithm Name\n"); 
                return KAT_HEADER_ERROR; 
        } 
        if ( FindMarker(fp_in, "# Principal Submitter:") ) { 
                fscanf(fp_in, "%[^\n]\n", line); 
        } 
        else { 
                printf("genLongMsg: Couldn't read Principal Submitter\n"); 
                return KAT_HEADER_ERROR; 
        } 

        done = 0; 
        timeTest(0, logFile);   //test start 
        do { 
                if ( FindMarker(fp_in, "Len = ") ) 
                        fscanf(fp_in, "%d", &msglen); 
                else 
                        break; 
                msgbytelen = (msglen+7)/8; 

                if ( !ReadHex(fp_in, Msg, msgbytelen, "Msg = ") ) { 
                        printf("ERROR: unable to read 'Msg' from <LongMsgKAT.txt>\n"); 
                        return KAT_DATA_ERROR; 
                } 
                timeTest(1, logFile);   //unit test start 
                Hash(hashbitlen, Msg, msglen, MD); 
                timeTest(2, logFile);   //unit test stop 
        } while ( !done ); 
        timeTest(3, logFile);   //test stop 
        fclose(logFile); 
        printf("\tfinished LongMsgKAT for <%d>\n", hashbitlen); 

        fclose(fp_in); 

        return KAT_SUCCESS; 
} 

STATUS_CODES 
genExtremelyLongMsg(int hashbitlen) 
{ 
        char            line[SUBMITTER_INFO_LEN]; 
        BitSequence     Text[65], MD[64]; 
        int                     i, repeat; 
        FILE            *fp_in; 
        hashState       state; 
        HashReturn      retval; 
        char            logFileName [100]; 
        FILE            *logFile; 

        sprintf(logFileName, "%s_genExtremelyLongMsg_%d.log", ALGORITHM_NAME, hashbitlen); 
        printf("logging results to: %s\n", logFileName); 
        logFile = fopen(logFileName, "w"); 

        if ( (fp_in = fopen("ExtremelyLongMsgKAT.txt", "r")) == NULL ) { 
                printf("Couldn't open <ExtremelyLongMsgKAT.txt> for read\n"); 
                return KAT_FILE_OPEN_ERROR; 
        } 

        if ( FindMarker(fp_in, "# Algorithm Name:") ) { 
                fscanf(fp_in, "%[^\n]\n", line); 
        } 
        else { 
                printf("genExtremelyLongMsg: Couldn't read Algorithm Name\n"); 
                return KAT_HEADER_ERROR; 
        } 
        if ( FindMarker(fp_in, "# Principal Submitter:") ) { 
                fscanf(fp_in, "%[^\n]\n", line); 
        } 
        else { 
                printf("genExtremelyLongMsg: Couldn't read Principal Submitter\n"); 
                return KAT_HEADER_ERROR; 
        } 


        if ( FindMarker(fp_in, "Repeat = ") ) 
                fscanf(fp_in, "%d", &repeat); 
        else { 
                printf("ERROR: unable to read 'Repeat' from <ExtremelyLongMsgKAT.txt>\n"); 
                return KAT_DATA_ERROR; 
        } 
 
        if ( FindMarker(fp_in, "Text = ") ) 
                fscanf(fp_in, "%s", Text); 
        else { 
                printf("ERROR: unable to read 'Text' from <ExtremelyLongMsgKAT.txt>\n"); 
                return KAT_DATA_ERROR; 
        } 

//      memcpy(Text, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno", 64); 
        timeTest(0, logFile);   //test start 
        timeTest(1, logFile);   //unit test start 
        if ( (retval = Init(&state, hashbitlen)) != KAT_SUCCESS ) { 
                printf("Init returned <%d> in genExtremelyLongMsg\n", retval); 
                return KAT_HASH_ERROR; 
        } 
        timeTest(2, logFile);   //unit test stop 
        for ( i=0; i<repeat; i++ ) 
        { 
                if (i % 8192 == 0) timeTest(1, logFile);        //unit test start 
                if ( (retval = Update(&state, Text, 512)) != KAT_SUCCESS ) { 
                        printf("Update returned <%d> in genExtremelyLongMsg\n", retval); 
                        return KAT_HASH_ERROR; 
                } 
                if (i % 8192 == 0) timeTest(2, logFile);        //unit test stop 
        } 
        timeTest(1, logFile);   //unit test start 
        if ( (retval = Final(&state, MD)) != KAT_SUCCESS ) { 
                printf("Final returned <%d> in genExtremelyLongMsg\n", retval); 
                return KAT_HASH_ERROR; 
        } 
        timeTest(2, logFile);   //unit test stop 
        timeTest(3, logFile);   //test stop 
        printf("\tfinished ExtremelyLongMsgKAT for <%d>\n", hashbitlen); 

        fclose(fp_in); 

        return KAT_SUCCESS; 
} 

STATUS_CODES 
genMonteCarlo(int hashbitlen) 
{ 
        char            line[SUBMITTER_INFO_LEN]; 
        BitSequence     Seed[128], Msg[128], MD[64], Temp[128]; 
        int                     i, j, bytelen; 
        FILE            *fp_in; 
        char            logFileName [100]; 
        FILE            *logFile; 

        sprintf(logFileName, "%s_genMonteCarlo_%d.log", ALGORITHM_NAME, hashbitlen); 
        printf("logging results to: %s\n", logFileName); 
        logFile = fopen(logFileName, "w"); 
 
        if ( (fp_in = fopen("MonteCarlo.txt", "r")) == NULL ) { 
                printf("Couldn't open <MonteCarlo.txt> for read\n"); 
                return KAT_FILE_OPEN_ERROR; 
        } 

        if ( FindMarker(fp_in, "# Algorithm Name:") ) { 
                fscanf(fp_in, "%[^\n]\n", line); 
        } 
        else { 
                printf("genMonteCarlo: Couldn't read Algorithm Name\n"); 
                return KAT_HEADER_ERROR; 
        } 
        if ( FindMarker(fp_in, "# Principal Submitter:") ) { 
                fscanf(fp_in, "%[^\n]\n", line); 
        } 
        else { 
                printf("genMonteCarlo: Couldn't read Principal Submitter\n"); 
                return KAT_HEADER_ERROR; 
        } 

        if ( !ReadHex(fp_in, Seed, 128, "Seed = ") ) { 
                printf("ERROR: unable to read 'Seed' from <MonteCarlo.txt>\n"); 
                return KAT_DATA_ERROR; 
        } 

        bytelen = hashbitlen / 8; 
        memcpy(Msg, Seed, 128); 
        timeTest(0, logFile);   //test start 
        for ( j=0; j<100; j++ ) { 
                for ( i=0; i<1000; i++ ) { 
                        timeTest(1, logFile);   //unit test start 
                        Hash(hashbitlen, Msg, 1024, MD); 
                        timeTest(2, logFile);   //unit test stop 
                        memcpy(Temp, Msg, 128-bytelen); 
                        memcpy(Msg, MD, bytelen); 
                        memcpy(Msg+bytelen, Temp, 128-bytelen); 
                } 
        } 
        timeTest(3, logFile);   //test stop 
        printf("\tfinished MonteCarloKAT for <%d>\n", hashbitlen); 

        fclose(fp_in); 

        return KAT_SUCCESS; 
} 

// 
// ALLOW TO READ HEXADECIMAL ENTRY (KEYS, DATA, TEXT, etc.) 
// 
int 
FindMarker(FILE *infile, const char *marker) 
{ 
        char    line[MAX_MARKER_LEN]; 
        int             i, len; 

        len = (int)strlen(marker); 
        if ( len > MAX_MARKER_LEN-1 ) 
                len = MAX_MARKER_LEN-1; 

        for ( i=0; i<len; i++ ) 
                if ( (line[i] = fgetc(infile)) == EOF ) 
                        return 0; 
        line[len] = '\0'; 

        while ( 1 ) { 
                if ( !strncmp(line, marker, len) ) 
                        return 1; 

                for ( i=0; i<len-1; i++ ) 
                        line[i] = line[i+1]; 
                if ( (line[len-1] = fgetc(infile)) == EOF ) 
                        return 0; 
                line[len] = '\0'; 
        } 

        // shouldn't get here 
        return 0; 
} 

// 
// ALLOW TO READ HEXADECIMAL ENTRY (KEYS, DATA, TEXT, etc.) 
// 
int 
ReadHex(FILE *infile, BitSequence *A, int Length, char *str) 
{ 
        int                     i, ch, started; 
        BitSequence     ich; 

        if ( Length == 0 ) { 
                A[0] = 0x00; 
                return 1; 
        } 
        memset(A, 0x00, Length); 
        started = 0; 
        if ( FindMarker(infile, str) ) 
                while ( (ch = fgetc(infile)) != EOF ) { 
                        if ( !isxdigit(ch) ) { 
                                if ( !started ) { 
                                        if ( ch == '\n' ) 
                                                break; 
                                        else 
                                                continue; 
                                } 
                                else 
                                        break; 
                        } 
                        started = 1; 
                        if ( (ch >= '0') && (ch <= '9') ) 
                                ich = ch - '0'; 
                        else if ( (ch >= 'A') && (ch <= 'F') ) 
                                ich = ch - 'A' + 10; 
                        else if ( (ch >= 'a') && (ch <= 'f') ) 
                                ich = ch - 'a' + 10; 

                        for ( i=0; i<Length-1; i++ ) 
                                A[i] = (A[i] << 4) | (A[i+1] >> 4); 
                        A[Length-1] = (A[Length-1] << 4) | ich; 
                } 
        else 
                return 0; 

        return 1; 
} 

void 
fprintBstr(FILE *fp, char *S, BitSequence *A, int L) 
{ 
        int             i; 

        fprintf(fp, "%s", S); 

        for ( i=0; i<L; i++ ) 
                fprintf(fp, "%02X", A[i]); 

        if ( L == 0 ) 
                fprintf(fp, "00"); 

        fprintf(fp, "\n"); 
} 
    
